home *** CD-ROM | disk | FTP | other *** search
Text File | 2000-10-06 | 16.2 KB | 508 lines | [TEXT/CWIE] |
- ///--------------------------------------------------------------------------------------
- // SWParticles.c - by Matthew Foodim, with changes by Anders Björklund and Vern Jensen.
- //
- // Note: NewParticle currently does not add any new particles if there is no room in the
- // gParticleArray.
- ///--------------------------------------------------------------------------------------
-
- #include <SWIncludes.h>
- #include <SWParticles.h>
-
- ///--------------------------------------------------------------------------------------
- // Global variables, private to this file
- ///--------------------------------------------------------------------------------------
-
- static ParticlePtr gParticleArray = NULL; // An array containing data for each particle.
- static long gLowestUnusedElement; // Start at this element when inserting a new particle
- static long gLastElementInUse; // The highest element that has an active particle.
- static long gNumberOfParticlesActive; // Number of array elements currently in use
- static SWFixed gGravity; // downward acceleration
- static unsigned long gNumArrayElements; // Size of array, unsigned to avoid the 68K optimizer bug
- static Boolean gParticlesInitialized = false; // Was InitParticles called successfully?
-
- ///--------------------------------------------------------------------------------------
- // Unroll code for the different depths, to avoid any per-pixel depth checks
- // Macros, to avoid the bug-prone huge amounts of copy and paste.
- // The optimizer will make it the same, anyway.
- // "Better living through pre-processor" --afb
- ///--------------------------------------------------------------------------------------
-
- #define FOREACH_DEPTH_DO(depth,instructions) \
- switch ( depth ) \
- { case 8: { int DEPTH=8; instructions } break; \
- case 16: { int DEPTH=16; instructions } break; \
- case 32: { int DEPTH=32; instructions } break; }
-
- #define GET_DEPTH_PIXEL(frame,x,y) \
- ( DEPTH==8 ? *SW_PIXELPTR8(frame,x,y) : \
- ( DEPTH==16 ? *SW_PIXELPTR16(frame,x,y) : \
- *SW_PIXELPTR32(frame,x,y) ))
-
- #define SET_DEPTH_PIXEL(frame,x,y,c) \
- if(DEPTH==8) *SW_PIXELPTR8(frame,x,y)=c; else \
- { if(DEPTH==16) *SW_PIXELPTR16(frame,x,y)=c; else \
- *SW_PIXELPTR32(frame,x,y)=c; }
-
- ///--------------------------------------------------------------------------------------
- // InitParticles
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC OSErr InitParticles(long maxNumParticles, SWFixed gravity)
- {
- OSErr err = noErr;
-
- gNumArrayElements = maxNumParticles;
- gGravity = gravity;
-
- // Allocate memory for the particle array
- gParticleArray = (ParticlePtr) NewPtr(sizeof(Particle) * gNumArrayElements);
-
- if (gParticleArray == NULL)
- {
- err = memFullErr;
- }
- else
- {
- ClearParticles();
- }
-
- gLowestUnusedElement = 0;
- gLastElementInUse = 0;
- gNumberOfParticlesActive = 0;
-
- if (err == noErr)
- gParticlesInitialized = true;
- else
- gParticlesInitialized = false;
-
- return err;
- }
-
-
- ///--------------------------------------------------------------------------------------
- // ClearParticles
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void ClearParticles( void )
- {
- long curParticle;
-
- SW_ASSERT(gParticleArray != NULL);
-
- for (curParticle = 0; curParticle < gNumArrayElements; curParticle++)
- {
- gParticleArray[curParticle].lifeRemaining = 0;
- }
-
- gLowestUnusedElement = 0;
- gLastElementInUse = 0;
- gNumberOfParticlesActive = 0;
- }
-
-
- ///--------------------------------------------------------------------------------------
- // NewParticle - Create a new particle
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void NewParticle(
- unsigned long color,
- SWFixed horizLoc,
- SWFixed vertLoc,
- SWFixed horizSpeed,
- SWFixed vertSpeed,
- short lifeRemaining)
- {
- if (gParticlesInitialized == false)
- return;
-
- // Add a particle only if there is room.
- if (gNumberOfParticlesActive < gNumArrayElements)
- {
- // Find the lowest unused array element
- // (Note: this assumes there *is* an available element.)
- while (gParticleArray[gLowestUnusedElement].lifeRemaining > 0)
- gLowestUnusedElement++;
-
- SW_ASSERT(gLowestUnusedElement < gNumArrayElements);
-
- gParticleArray[gLowestUnusedElement].lifeRemaining = lifeRemaining;
- gParticleArray[gLowestUnusedElement].color = color;
- gParticleArray[gLowestUnusedElement].horizSpeed = horizSpeed;
- gParticleArray[gLowestUnusedElement].vertSpeed = vertSpeed;
- gParticleArray[gLowestUnusedElement].horizLoc = horizLoc;
- gParticleArray[gLowestUnusedElement].vertLoc = vertLoc;
- gParticleArray[gLowestUnusedElement].oldHorizLoc = horizLoc;
- gParticleArray[gLowestUnusedElement].oldVertLoc = vertLoc;
-
- // If the new particle is higher than gLastElementInUse, extend it.
- if (gLowestUnusedElement > gLastElementInUse)
- gLastElementInUse = gLowestUnusedElement;
-
- gNumberOfParticlesActive++;
- gLowestUnusedElement++;
- }
- }
-
- #pragma mark -
-
- ///--------------------------------------------------------------------------------------
- // EraseParticlesOffscreen
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void EraseParticlesOffscreen( SpriteWorldPtr spriteWorldP )
- {
- Particle *particlePtr;
- FramePtr workFrameP = spriteWorldP->workFrameP,
- backFrameP = spriteWorldP->backFrameP;
- int visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
- int horizLoc, vertLoc;
- long index, count = gLastElementInUse;
-
- SW_ASSERT(spriteWorldP != NULL);
- SW_ASSERT(workFrameP != NULL && workFrameP->isFrameLocked);
- SW_ASSERT(workFrameP->frameDepth >= 8);
- SW_ASSERT(gParticlesInitialized == true);
-
- visBoundsLeft = workFrameP->frameRect.left;
- visBoundsRight = workFrameP->frameRect.right;
- visBoundsTop = workFrameP->frameRect.top;
- visBoundsBottom = workFrameP->frameRect.bottom;
-
- START_32_BIT_MODE
-
- FOREACH_DEPTH_DO
- (
- spriteWorldP->pixelDepth,
- for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
- {
- if (particlePtr->lifeRemaining>0)
- {
- horizLoc = SW_FIX2INT(particlePtr->oldHorizLoc);
- vertLoc = SW_FIX2INT(particlePtr->oldVertLoc);
-
- if ((horizLoc<visBoundsRight)&&(horizLoc>visBoundsLeft)&&
- (vertLoc<visBoundsBottom)&&(vertLoc>visBoundsTop))
- {
- SET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc ,
- GET_DEPTH_PIXEL( backFrameP, horizLoc, vertLoc ) )
- }
- }
- }
- )
-
- END_32_BIT_MODE
- }
-
-
- ///--------------------------------------------------------------------------------------
- // EraseParticlesScrollingOffscreen
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void EraseParticlesScrollingOffscreen( SpriteWorldPtr spriteWorldP )
- {
- Particle *particlePtr;
- FramePtr workFrameP = spriteWorldP->workFrameP,
- backFrameP = spriteWorldP->backFrameP;
- int visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
- int horizLoc, vertLoc;
- long index, count = gLastElementInUse;
-
- SW_ASSERT(spriteWorldP != NULL);
- SW_ASSERT(spriteWorldP->workFrameP != NULL && workFrameP->isFrameLocked);
- SW_ASSERT(workFrameP->frameDepth >= 8);
- SW_ASSERT(gParticlesInitialized == true);
-
- visBoundsLeft = spriteWorldP->oldVisScrollRect.left;
- visBoundsRight = spriteWorldP->oldVisScrollRect.right;
- visBoundsTop = spriteWorldP->oldVisScrollRect.top;
- visBoundsBottom = spriteWorldP->oldVisScrollRect.bottom;
-
- START_32_BIT_MODE
-
- FOREACH_DEPTH_DO
- (
- spriteWorldP->pixelDepth,
- for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
- {
- if (particlePtr->lifeRemaining>0)
- {
- horizLoc = SW_FIX2INT(particlePtr->oldHorizLoc);
- vertLoc = SW_FIX2INT(particlePtr->oldVertLoc);
-
- if ((horizLoc<visBoundsRight)&&(horizLoc>visBoundsLeft)&&
- (vertLoc<visBoundsBottom)&&(vertLoc>visBoundsTop))
- {
- // Make the particle's points local to the offscreen area
- // we have to offset to the *old* ScrollRectOffset - which we have to calculate
- horizLoc -= spriteWorldP->backRect.right * (spriteWorldP->oldVisScrollRect.left / spriteWorldP->backRect.right);
- vertLoc -= spriteWorldP->backRect.bottom * (spriteWorldP->oldVisScrollRect.top / spriteWorldP->backRect.bottom);
-
- // Wrap the pixel if it's hanging off one side of the offscreen area
- if (horizLoc >= workFrameP->frameRect.right)
- horizLoc -= workFrameP->frameRect.right;
- else if (horizLoc < workFrameP->frameRect.left)
- horizLoc += workFrameP->frameRect.right;
-
- if (vertLoc >= workFrameP->frameRect.bottom)
- vertLoc -= workFrameP->frameRect.bottom;
- else if (vertLoc < workFrameP->frameRect.top)
- vertLoc += workFrameP->frameRect.bottom;
-
- SET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc ,
- GET_DEPTH_PIXEL( backFrameP, horizLoc, vertLoc ) )
- }
- }
- }
- )
-
- END_32_BIT_MODE
-
- }
-
-
- ///--------------------------------------------------------------------------------------
- // DrawParticlesOffscreen
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void DrawParticlesOffscreen( SpriteWorldPtr spriteWorldP )
- {
- Particle *particlePtr;
- FramePtr workFrameP = spriteWorldP->workFrameP;
- int visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
- int horizLoc, vertLoc;
- long index, count = gLastElementInUse;
-
- SW_ASSERT(spriteWorldP != NULL);
- SW_ASSERT(workFrameP != NULL && workFrameP->isFrameLocked);
- SW_ASSERT(workFrameP->frameDepth >= 8);
- SW_ASSERT(gParticlesInitialized == true);
-
- visBoundsLeft = workFrameP->frameRect.left;
- visBoundsRight = workFrameP->frameRect.right;
- visBoundsTop = workFrameP->frameRect.top;
- visBoundsBottom = workFrameP->frameRect.bottom;
-
- START_32_BIT_MODE
-
- FOREACH_DEPTH_DO
- (
- spriteWorldP->pixelDepth,
- for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
- {
- if (particlePtr->lifeRemaining>1)
- {
- horizLoc = SW_FIX2INT(particlePtr->horizLoc);
- vertLoc = SW_FIX2INT(particlePtr->vertLoc);
-
- if ((horizLoc<visBoundsRight)&&(horizLoc>visBoundsLeft)&&
- (vertLoc<visBoundsBottom)&&(vertLoc>visBoundsTop))
- {
- SET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc, particlePtr->color);
- }
- }
- }
- )
-
- END_32_BIT_MODE
-
- }
-
-
- ///--------------------------------------------------------------------------------------
- // DrawParticlesScrollingOffscreen
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void DrawParticlesScrollingOffscreen( SpriteWorldPtr spriteWorldP )
- {
- Particle *particlePtr;
- FramePtr workFrameP = spriteWorldP->workFrameP;
- int visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
- int horizLoc, vertLoc;
- long index, count = gLastElementInUse;
-
- SW_ASSERT(spriteWorldP != NULL);
- SW_ASSERT(workFrameP != NULL && workFrameP->isFrameLocked);
- SW_ASSERT(workFrameP->frameDepth >= 8);
- SW_ASSERT(gParticlesInitialized == true);
-
- visBoundsLeft = spriteWorldP->visScrollRect.left;
- visBoundsRight = spriteWorldP->visScrollRect.right;
- visBoundsTop = spriteWorldP->visScrollRect.top;
- visBoundsBottom = spriteWorldP->visScrollRect.bottom;
-
- START_32_BIT_MODE
-
- FOREACH_DEPTH_DO
- (
- spriteWorldP->pixelDepth,
- for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
- {
- if (particlePtr->lifeRemaining>1)
- {
- horizLoc = SW_FIX2INT(particlePtr->horizLoc);
- vertLoc = SW_FIX2INT(particlePtr->vertLoc);
-
- if ((horizLoc<visBoundsRight)&&(horizLoc>visBoundsLeft)&&
- (vertLoc<visBoundsBottom)&&(vertLoc>visBoundsTop))
- {
- // Make the particle's points local to the offscreen area
- horizLoc -= spriteWorldP->horizScrollRectOffset;
- vertLoc -= spriteWorldP->vertScrollRectOffset;
-
- // Wrap the pixel if it's hanging off one side of the offscreen area
- if (horizLoc >= workFrameP->frameRect.right)
- horizLoc -= workFrameP->frameRect.right;
- else if (horizLoc < workFrameP->frameRect.left)
- horizLoc += workFrameP->frameRect.right;
-
- if (vertLoc >= workFrameP->frameRect.bottom)
- vertLoc -= workFrameP->frameRect.bottom;
- else if (vertLoc < workFrameP->frameRect.top)
- vertLoc += workFrameP->frameRect.bottom;
-
- SET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc, particlePtr->color );
- }
- }
- }
- )
-
- END_32_BIT_MODE
-
- }
-
- #pragma mark -
-
- ///--------------------------------------------------------------------------------------
- // UpdateParticlesInWindow - moves particles, and also draws them to the window
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void UpdateParticlesInWindow( SpriteWorldPtr spriteWorldP )
- {
- Particle *particlePtr;
- FramePtr workFrameP = spriteWorldP->workFrameP,
- windowFrameP = spriteWorldP->windowFrameP;
- int visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
- int horizLoc, vertLoc;
- long index, count = gLastElementInUse;
-
- SW_ASSERT(spriteWorldP != NULL);
- SW_ASSERT(windowFrameP != NULL && windowFrameP->isFrameLocked);
- SW_ASSERT(windowFrameP->frameDepth >= 8);
- SW_ASSERT(gParticlesInitialized == true);
-
- visBoundsLeft = windowFrameP->frameRect.left;
- visBoundsRight = windowFrameP->frameRect.right;
- visBoundsTop = windowFrameP->frameRect.top;
- visBoundsBottom = windowFrameP->frameRect.bottom;
-
- START_32_BIT_MODE
-
- FOREACH_DEPTH_DO
- (
- spriteWorldP->pixelDepth,
- for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
- {
- if (particlePtr->lifeRemaining > 0)
- {
- horizLoc = SW_FIX2INT(particlePtr->oldHorizLoc);
- vertLoc = SW_FIX2INT(particlePtr->oldVertLoc);
-
- // First erase the particle from its old position
- if ((horizLoc < visBoundsRight) && (horizLoc > visBoundsLeft) &&
- (vertLoc < visBoundsBottom) && (vertLoc > visBoundsTop))
- {
- SET_DEPTH_PIXEL( windowFrameP, horizLoc, vertLoc ,
- GET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc ) )
- }
-
- horizLoc = SW_FIX2INT(particlePtr->horizLoc);
- vertLoc = SW_FIX2INT(particlePtr->vertLoc);
-
- // Now draw the particle in its new position
- if ((horizLoc < visBoundsRight) && (horizLoc > visBoundsLeft) &&
- (vertLoc < visBoundsBottom) && (vertLoc > visBoundsTop))
- {
- SET_DEPTH_PIXEL( windowFrameP, horizLoc, vertLoc ,
- GET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc ) )
- }
- }
- }
- )
-
- END_32_BIT_MODE
-
- // Move them *after* drawing them!
- MoveParticles();
- }
-
-
- ///--------------------------------------------------------------------------------------
- // UpdateParticlesInScrollingWindow - moves particles, but does not need to draw them,
- // since the entire offscreen is copied to the screen in a scrolling animation.
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void UpdateParticlesInScrollingWindow( SpriteWorldPtr spriteWorldP )
- {
- #pragma unused(spriteWorldP)
- SW_ASSERT(gParticlesInitialized == true);
-
- // we do not need to draw them here, since the entire offscreen is copied to the screen
-
- MoveParticles();
- }
-
-
- ///--------------------------------------------------------------------------------------
- // MoveParticles - called internally by UpdateParticlesInScrollingWindow and
- // UpdateParticlesInWindow.
- ///--------------------------------------------------------------------------------------
-
- SW_FUNC void MoveParticles( void )
- {
- Particle *particlePtr;
- long index, count = gLastElementInUse;
-
- if (gParticlesInitialized == false)
- return;
-
- for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
- {
- SW_ASSERT(index < gNumArrayElements);
-
- if (particlePtr->lifeRemaining > 0)
- {
- particlePtr->lifeRemaining--;
-
- if (particlePtr->lifeRemaining == 0)
- {
- gNumberOfParticlesActive--;
-
- // If this dead particle opened up a space, update gLowestUnusedElement.
- if (index < gLowestUnusedElement)
- gLowestUnusedElement = index;
- }
- else
- {
- particlePtr->oldHorizLoc = particlePtr->horizLoc;
- particlePtr->oldVertLoc = particlePtr->vertLoc;
-
- particlePtr->horizLoc += particlePtr->horizSpeed;
- particlePtr->vertLoc += particlePtr->vertSpeed;
-
- particlePtr->vertSpeed += gGravity;
- }
- }
- }
-
- if (gNumberOfParticlesActive <= 0)
- {
- gLastElementInUse = 0;
- }
- else
- {
- // See if the gLastElementInUse isn't in use any more.
- // If not, find the next element that still is.
- while (gParticleArray[gLastElementInUse].lifeRemaining == 0 && gLastElementInUse > 0)
- gLastElementInUse--;
- }
- }